/**
 * @ProjectName:Backend
 * @Title: JwtFilter
 * @Package com.phrp.shiro
 * @Description: 自定义拦截器类，重写shiro拦截器的实现
 * @author SuShuChao
 * @date 2024/4/17 15:04
 * @version V1.0
 * Copyright (c) 2024, 2034279227@qq.com All Rights Reserved.
 */
package com.phrp.shiro;

import cn.hutool.json.JSONUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtFilter extends AuthenticatingFilter{

    /**
     * 重写shiro的生成token的方法 (利用jwt生成自定义的token) 此后shiro会通过这个token进行login
     * @return
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        //将请求转换为HttpServletRequest
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        //获取请求头中的jwt信息
        String jwt = request.getHeader("Authorization");
        if (StringUtils.isEmpty(jwt)) {
            return null;
        }
        return new JwtToken(jwt);
    }

    /**
     * 拦截校验token
     * @param servletRequest
     * @param servletResponse
     * @param mappedValue
     * @return
     */
    @Override
    public boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) {
        //Always return true if the request’s method is OPTIONS
        if (servletRequest instanceof HttpServletRequest) {
            if (((HttpServletRequest) servletRequest).getMethod().toUpperCase().equals("OPTIONS")) {
                return true;
            }
        }
        return false;
    }

    /**
     * 拒绝访问的请求会进入这个方法处理
     * isAccessAllowed()方法返回false,会进入该方法，表示拒绝访问
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        //将请求转换为HttpServletRequest
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader("Authorization");
        //获取请求头中的jwt信息
        if (StringUtils.isEmpty(jwt)) {
            //HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
            //httpResponse.getWriter().print(JSONUtil.toJsonStr(Result.isfail("token已过期,请重新登录")));
            return onLoginFailure(null,new ExpiredCredentialsException("请登录后访问该资源"),servletRequest,servletResponse);
            //jwt为空后 不需要拦截 通过后接口会通过注解进行异常处理
            //return true;
        }
        //执行登录
        return executeLogin(servletRequest, servletResponse);
    }


    /**
     * 重写登录失败的方法 失败后自定义失败响应信息
     * @param token
     * @param e
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        Throwable throwable = e.getCause() == null ? e : e.getCause(); // 获取异常错误信息
        String jsonfail = JSONUtil.toJsonStr(throwable.getMessage());
        HttpServletResponse servletResponse = (HttpServletResponse) response;
        try {
            //解决传输中文乱码问题
            servletResponse.setHeader("Content-Type","text/plain;charset=UTF-8");
            servletResponse.getWriter().print(jsonfail);
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
        return false;
    }

    /**
     * 处理过滤器跨域问题
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,PUT");
        httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        //跨域时会首先发送一个option请求，这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())){
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request,response);
    }
}
